#![feature(generic_const_exprs)]
#![allow(incomplete_features)]


use std::path::Path;

use bevy::asset::RenderAssetUsages;
use bevy::input::mouse::{MouseMotion, MouseWheel};
use bevy::mesh::Indices;
use bevy::prelude::*;
use bevy::render::render_resource::PrimitiveTopology;

use bevy_simple_voxmarch::voxel::layout::{chunk_voxel_count, region_chunk_count};
use bevy_simple_voxmarch::utils::const_assert::{ConstAssert, IsTrue};
use serde::de::DeserializeOwned;
use serde::{Deserialize, Serialize};

use bevy_simple_voxmarch::voxel::chunk::Chunk;
use bevy_simple_voxmarch::voxel::world::{RegionId, World};
use std::collections::HashSet;
use std::hash::{Hash, Hasher};

// ------------------------------------------------------------
// 附加到体素里的用户自定义数据
// ------------------------------------------------------------
#[derive(Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Debug, Default)]
pub struct TileData {
    pub data: u8,
}

// ------------------------------------------------------------
// 简单飞行相机组件+系统
// ------------------------------------------------------------
#[derive(Component)]
struct FlyCam {
    speed: f32,
    sensitivity: f32,
    yaw: f32,
    pitch: f32,
}

impl Default for FlyCam {
    fn default() -> Self {
        Self {
            speed: 30.0, // 加快一点，地图大了要飞快点
            sensitivity: 0.003,
            yaw: 0.0,
            pitch: 0.0,
        }
    }
}

// ------------------------------------------------------------
// App 入口
// ------------------------------------------------------------
fn main() {
    App::new()
        .add_plugins(DefaultPlugins)
        .add_systems(Startup, setup_full_world)
        .add_systems(Update, (fly_camera_system, camera_speed_control))
        .run();
}

// ------------------------------------------------------------
// 我们的主场景搭建：
// 1. 生成大范围 chunk
// 2. 写地形密度（平原 + 中央大山坡）
// 3. meshing -> Bevy Mesh
// 4. 灯光 + 相机
// ------------------------------------------------------------

fn setup_full_world(
    mut commands: Commands,
    mut meshes: ResMut<Assets<bevy::prelude::Mesh>>,
    mut materials: ResMut<Assets<StandardMaterial>>,
) {
    println!("[setup] start (full-world unified coords)");

    const RBITS: u8 = 5;
    const CBITS: u8 = 5;

    // 地形参数
    let gen_params = GenParams {
        hill_radius_world: 200.0,
        hill_height_world: 60.0,
        ground_base_height: 8.0,
    };

    // 世界对象
    std::fs::create_dir_all("world").expect("cannot create world dir");
    let mut world =
        World::<RBITS, CBITS, TileData>::new(Path::new("world")).expect("World::new failed");

    // 生成一大片 world，并拿到成功生成/写密度的 chunk 列表
    let range_xz: i32 = 10;
    let generated_chunks =
        generate_world_area::<RBITS, CBITS, TileData>(&mut world, range_xz, gen_params);

    println!("[setup] meshing generated chunks ...");

    let mut spawned_mesh_count = 0usize;

    // 遍历“确实生成过”的每个 chunk，而不是自己猜 for 循环
    for ch in generated_chunks.iter() {
        // 还原这个 chunk 的世界体素原点
        let (wx0, wy0, wz0) = world.chunk_world_origin_from_region_local(
            ch.region_id,
            ch.clx,
            ch.cly,
            ch.clz,
        );

        if let Ok(voxel_mesh) = world.meshify_chunk(wx0, wy0, wz0) {
            if voxel_mesh.indices.is_empty() || voxel_mesh.positions.is_empty() {
                continue;
            }

            // 建 Bevy Mesh
            let mut bevy_mesh = bevy::prelude::Mesh::new(
                PrimitiveTopology::TriangleList,
                RenderAssetUsages::RENDER_WORLD,
            );

            bevy_mesh.insert_attribute(Mesh::ATTRIBUTE_POSITION, voxel_mesh.positions.clone());
            bevy_mesh.insert_attribute(Mesh::ATTRIBUTE_NORMAL, voxel_mesh.normals.clone());
            bevy_mesh.insert_indices(Indices::U32(voxel_mesh.indices.clone()));

            // 直接放世界坐标，不加 Transform 偏移
            commands.spawn((
                Mesh3d(meshes.add(bevy_mesh)),
                MeshMaterial3d(materials.add(StandardMaterial {
                    base_color: Color::srgb(0.5, 0.7, 0.4),
                    perceptual_roughness: 0.9,
                    metallic: 0.0,
                    ..default()
                })),
            ));

            spawned_mesh_count += 1;
        }
    }

    println!("[setup] spawned {} chunk meshes", spawned_mesh_count);

    // 灯光
    commands.spawn((
        DirectionalLight {
            illuminance: 30000.0,
            shadows_enabled: false,
            ..default()
        },
        Transform::from_xyz(200.0, 400.0, 200.0).looking_at(Vec3::ZERO, Vec3::Y),
    ));

    // 环境光
    commands.insert_resource(AmbientLight {
        color: Color::WHITE,
        brightness: 1000.0,
        #[allow(unused)]
        affects_lightmapped_meshes: true,
    });

    // 摄像机放在对角上方一点，让你能看见整块地形
    commands.spawn((
        Camera3d::default(),
        Transform::from_xyz(300.0, 200.0, 300.0).looking_at(
            Vec3::new(
                0.0,
                gen_params.ground_base_height + gen_params.hill_height_world * 0.6,
                0.0,
            ),
            Vec3::Y,
        ),
        FlyCam::default(),
    ));

    println!(
        "[setup] scene ready. WASD/Space/Shift fly, RMB look, wheel speed"
    );
}

// ------------------------------------------------------------
// 自由飞行相机控制系统
// ------------------------------------------------------------
fn fly_camera_system(
    time: Res<Time>,
    keyboard: Res<ButtonInput<KeyCode>>,
    mouse_button: Res<ButtonInput<MouseButton>>,
    mut mouse_motion: MessageReader<MouseMotion>,
    mut query: Query<(&mut Transform, &mut FlyCam)>,
) {
    for (mut transform, mut flycam) in query.iter_mut() {
        // 键盘位移
        let mut velocity = Vec3::ZERO;
        let forward = *transform.forward();
        let right = *transform.right();
        let up = Vec3::Y;

        if keyboard.pressed(KeyCode::KeyW) {
            velocity += forward;
        }
        if keyboard.pressed(KeyCode::KeyS) {
            velocity -= forward;
        }
        if keyboard.pressed(KeyCode::KeyD) {
            velocity += right;
        }
        if keyboard.pressed(KeyCode::KeyA) {
            velocity -= right;
        }
        if keyboard.pressed(KeyCode::Space) {
            velocity += up;
        }
        if keyboard.pressed(KeyCode::ShiftLeft) {
            velocity -= up;
        }

        if velocity.length_squared() > 0.0 {
            let step = flycam.speed * time.delta_secs();
            transform.translation += velocity.normalize() * step;
        }

        // 鼠标视角（按住右键才旋转）
        if mouse_button.pressed(MouseButton::Right) {
            for motion in mouse_motion.read() {
                flycam.yaw -= motion.delta.x * flycam.sensitivity;
                flycam.pitch -= motion.delta.y * flycam.sensitivity;

                // pitch 限制，避免相机翻折
                flycam.pitch = flycam.pitch.clamp(-1.5, 1.5);

                transform.rotation = Quat::from_euler(EulerRot::YXZ, flycam.yaw, flycam.pitch, 0.0);
            }
        } else {
            // 没按右键就清掉积累的鼠标事件
            mouse_motion.clear();
        }
    }
}

// ------------------------------------------------------------
// 相机速度调节（鼠标滚轮）
// ------------------------------------------------------------
fn camera_speed_control(mut mouse_wheel: MessageReader<MouseWheel>, mut query: Query<&mut FlyCam>) {
    for mut flycam in query.iter_mut() {
        for wheel in mouse_wheel.read() {
            flycam.speed += wheel.y * 10.0;
            flycam.speed = flycam.speed.clamp(5.0, 2000.0);
        }
    }
}

#[derive(Clone, Copy)]
struct GenParams {
    hill_radius_world: f32,
    hill_height_world: f32,
    ground_base_height: f32,
}

// 返回给定世界体素 (wx,wy,wz) 的标量场值：
// <0 = 实心土地，>0 = 空气，=0 ≈ 表面
#[inline]
fn terrain_sdf(p: GenParams, wx: i32, wy: i32, wz: i32) -> f32 {
    let x = wx as f32;
    let y = wy as f32;
    let z = wz as f32;

    // 距离世界中心(0,0)的径向距离
    let dist_xz = (x * x + z * z).sqrt();

    // t=0 在中心，t=1 在山坡边缘
    let t = (dist_xz / p.hill_radius_world).clamp(0.0, 1.0);

    // 圆鼓包曲线 (1 - t^2)^2
    let hill_profile = (1.0 - t * t).powi(2);

    // 山体抬升
    let hill_offset = p.hill_height_world * hill_profile;

    // 该点应该的地表高度
    let surface_height = p.ground_base_height + hill_offset;

    // SDF: 点的y - 地表y
    // y < surface => 负 => 实心
    y - surface_height
}

fn generate_world_area<
    const RBITS: u8,
    const CBITS: u8,
    DATA: Clone + Serialize + DeserializeOwned + Default,
>(
    world: &mut World<RBITS, CBITS, DATA>,
    range_xz: i32,
    gen_params: GenParams,
) -> HashSet<ChunkHandle>
where
    [(); region_chunk_count::<RBITS>()]:,
    [(); chunk_voxel_count::<CBITS>()]:,
    ConstAssert<{ RBITS <= 8 }>: IsTrue,
    ConstAssert<{ CBITS <= 8 }>: IsTrue,
{
    let chunk_size_vox: i32 = 1 << CBITS;
    let chunks_per_region: i32 = 1 << RBITS; // 32

    // y 范围（按 chunk index）
    let min_surface_h = gen_params.ground_base_height - gen_params.hill_height_world;
    let max_surface_h = gen_params.ground_base_height + gen_params.hill_height_world;

    let to_chunk_y = |world_y: f32| -> i32 {
        let yi = world_y.floor() as i32;
        if (yi >= 0) || (yi % chunk_size_vox == 0) {
            yi / chunk_size_vox
        } else {
            (yi / chunk_size_vox) - 1
        }
    };

    let cy_surface_min = to_chunk_y(min_surface_h);
    let cy_surface_max = to_chunk_y(max_surface_h);

    let cy_alloc_min = cy_surface_min - 1;
    let cy_alloc_max = cy_surface_max + 1;

    println!(
        "[gen] begin world gen: cx/cz in [-{0}..={0}], cy in [{1}..={2}]",
        range_xz, cy_alloc_min, cy_alloc_max
    );

    // 我们把成功生成的 chunk 记下来
    let mut generated: HashSet<ChunkHandle> = HashSet::new();

    for cz_global in -range_xz..=range_xz {
        for cx_global in -range_xz..=range_xz {
            for cy_global in cy_alloc_min..=cy_alloc_max {
                // region 坐标 (rix,riy,riz) = floor(global_chunk / 32)
                let rix = floor_div_i32(cx_global, chunks_per_region);
                let riy = floor_div_i32(cy_global, chunks_per_region);
                let riz = floor_div_i32(cz_global, chunks_per_region);

                // chunk 在 region 内的局部坐标 0..31
                let clx = floor_mod_i32(cx_global, chunks_per_region) as u32;
                let cly = floor_mod_i32(cy_global, chunks_per_region) as u32;
                let clz = floor_mod_i32(cz_global, chunks_per_region) as u32;

                let region_id = RegionId(rix, riy, riz);

                // 这个 chunk 在世界体素系的原点
                let (wx0, wy0, wz0) =
                    world.chunk_world_origin_from_region_local(region_id, clx, cly, clz);

                // region
                let Some(region) = world.get_region_mut(region_id) else {
                    continue;
                };

                // chunk
                let Some(chunk) = region.get_or_create_chunk_mut(clx, cly, clz) else {
                    continue;
                };

                // 写入密度场
                for iy in 0..chunk_size_vox {
                    let wyv = wy0 + iy as i32;
                    for iz in 0..chunk_size_vox {
                        let wzv = wz0 + iz as i32;
                        for ix in 0..chunk_size_vox {
                            let wxv = wx0 + ix as i32;

                            let d = terrain_sdf(gen_params, wxv, wyv, wzv);
                            let lin = Chunk::<CBITS, DATA>::chunk_vertex_linear_index(
                                ix as u32,
                                iy as u32,
                                iz as u32,
                            );
                            chunk.densities[lin] = d;
                        }
                    }
                }

                // 记录下来：这个 chunk 是真的存在/有数据的
                generated.insert(ChunkHandle {
                    region_id,
                    clx,
                    cly,
                    clz,
                });
            }
        }
    }

    println!("[gen] world gen done.");
    generated
}

#[inline]
fn floor_div_i32(a: i32, m: i32) -> i32 {
    // 数学上的 floor(a / m) ，对负数也正确
    // Rust 的 `/` 是向零截断，所以要修正
    if (a >= 0) || (a % m == 0) {
        a / m
    } else {
        // a < 0 且不能整除
        (a / m) - 1
    }
}

#[inline]
fn floor_mod_i32(a: i32, m: i32) -> i32 {
    // 数学上的 a mod m ，范围 [0, m)
    let r = a % m;
    if r < 0 { r + m } else { r }
}

#[derive(Clone, Copy, Debug)]
struct ChunkHandle {
    region_id: RegionId,
    clx: u32,
    cly: u32,
    clz: u32,
}

// 为了能放进 HashSet
impl PartialEq for ChunkHandle {
    fn eq(&self, other: &Self) -> bool {
        self.region_id == other.region_id
            && self.clx == other.clx
            && self.cly == other.cly
            && self.clz == other.clz
    }
}
impl Eq for ChunkHandle {}
impl Hash for ChunkHandle {
    fn hash<H: Hasher>(&self, state: &mut H) {
        self.region_id.hash(state);
        self.clx.hash(state);
        self.cly.hash(state);
        self.clz.hash(state);
    }
}

